package cn.com.anypay.manager.common.exception;

import cn.com.anypay.manager.common.response.ApiResponse;
import cn.com.anypay.manager.common.enums.ResultCode;
import cn.com.anypay.manager.miaoma.syserrorlog.SysErrorLogService;
import cn.com.anypay.manager.miaoma.sysuser.SysUserService;
import cn.com.anypay.manager.miaoma.sysuser.SysUserEntity;
import cn.com.anysdk.oss.exception.OssException;
import cn.com.anysdk.map.exception.MapException;
import cn.com.anysdk.sms.exception.SmsException;
import cn.com.anysdk.ocr.exception.OcrException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import io.sentry.Sentry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * 统一处理系统中的各种异常
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    @Autowired
    private SysErrorLogService sysErrorLogService;
    
    @Autowired
    private SysUserService sysUserService;

    /**
     * 处理业务异常
     */
    // Sa-Token 未登录异常
    @ExceptionHandler(NotLoginException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ApiResponse<Object> handleNotLoginException(NotLoginException e) {
        return ApiResponse.fail(ResultCode.UNAUTHORIZED, "请先登录");
    }

    // Sa-Token 权限不足异常
    @ExceptionHandler(NotPermissionException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ApiResponse<Object> handleNotPermissionException(NotPermissionException e) {
        return ApiResponse.fail(ResultCode.FORBIDDEN, "权限不足，无法访问该资源");
    }

    // Sa-Token 角色不足异常
    @ExceptionHandler(NotRoleException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ApiResponse<Object> handleNotRoleException(NotRoleException e) {
        return ApiResponse.fail(ResultCode.FORBIDDEN, "角色权限不足，无法访问该资源");
    }

    // 微信认证异常
    @ExceptionHandler(WeChatAuthException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResponse<Object> handleWeChatAuthException(WeChatAuthException e, HttpServletRequest request) {
        logger.warn("微信认证异常: {} - {}", request.getRequestURI(), e.getMessage());

        // 记录异常日志
        try {
            SysUserEntity currentUser = getCurrentUser();
            String username = currentUser != null ? currentUser.getUsername() : "unknown";
            String realName = currentUser != null ? currentUser.getRealName() : "未知用户";
            sysErrorLogService.recordErrorLog(e, request, username, realName,
                "微信认证", "微信认证异常处理", "处理微信认证异常", null, "wechat_auth", "微信认证异常");
        } catch (Exception logException) {
            logger.error("记录微信认证异常日志失败", logException);
        }

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "WeChatAuthException");
            scope.setTag("wechat.error.code", e.getWeChatErrorCode());
            scope.setTag("request.uri", request.getRequestURI());
            scope.setTag("request.method", request.getMethod());
            scope.setExtra("weChatErrorCode", e.getWeChatErrorCode());
            scope.setExtra("weChatErrorMsg", e.getWeChatErrorMsg());
            scope.setExtra("message", e.getMessage());
            scope.setExtra("timestamp", java.time.Instant.now().toString());
            scope.setLevel(io.sentry.SentryLevel.WARNING);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(e.getResultCode(), e.getMessage());
    }

    // 业务异常
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResponse<Object> handleBusinessException(BusinessException e, HttpServletRequest request) {
        logger.warn("业务异常: {} - {}", request.getRequestURI(), e.getMessage());

        // 记录异常日志
        try {
            SysUserEntity currentUser = getCurrentUser();
            String username = currentUser != null ? currentUser.getUsername() : "unknown";
            String realName = currentUser != null ? currentUser.getRealName() : "未知用户";
            sysErrorLogService.recordErrorLog(e, request, username, realName,
                "业务处理", "业务异常处理", "处理业务异常", null, "business", "业务异常");
        } catch (Exception logException) {
            logger.error("记录业务异常日志失败", logException);
        }

        // 发送到 Sentry（业务异常通常不需要上报，但可以根据需要调整）
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "BusinessException");
            scope.setTag("result.code", e.getResultCode().toString());
            scope.setExtra("resultCode", e.getResultCode().toString());
            scope.setExtra("message", e.getMessage());
            scope.setExtra("timestamp", java.time.Instant.now().toString());
            scope.setLevel(io.sentry.SentryLevel.WARNING);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(e.getResultCode(), e.getMessage());
    }

    /**
     * 处理OSS异常
     */
    @ExceptionHandler(OssException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResponse<Object> handleOssException(OssException e, HttpServletRequest request) {
        logger.error("OSS操作异常: {} - {}", request.getRequestURI(), e.getMessage(), e);

        // 记录异常日志
        try {
            SysUserEntity currentUser = getCurrentUser();
            String username = currentUser != null ? currentUser.getUsername() : "unknown";
            String realName = currentUser != null ? currentUser.getRealName() : "未知用户";
            sysErrorLogService.recordErrorLog(e, request, username, realName,
                "存储服务", "OSS异常处理", "处理OSS存储异常", null, "oss", "OSS存储异常");
        } catch (Exception logException) {
            logger.error("记录OSS异常日志失败", logException);
        }

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "oss");
            scope.setTag("request.uri", request.getRequestURI());
            scope.setTag("request.method", request.getMethod());
            scope.setLevel(io.sentry.SentryLevel.ERROR);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(ResultCode.SYSTEM_ERROR, "存储服务异常: " + e.getMessage());
    }

    /**
     * 处理地图异常
     */
    @ExceptionHandler(MapException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResponse<Object> handleMapException(MapException e, HttpServletRequest request) {
        logger.error("地图服务异常: {} - {}", request.getRequestURI(), e.getMessage(), e);

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "map");
            scope.setTag("request.uri", request.getRequestURI());
            scope.setTag("request.method", request.getMethod());
            scope.setLevel(io.sentry.SentryLevel.ERROR);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(ResultCode.SYSTEM_ERROR, "地图服务异常: " + e.getMessage());
    }

    /**
     * 处理短信异常
     */
    @ExceptionHandler(SmsException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResponse<Object> handleSmsException(SmsException e, HttpServletRequest request) {
        logger.error("短信服务异常: {} - {}", request.getRequestURI(), e.getMessage(), e);

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "sms");
            scope.setTag("request.uri", request.getRequestURI());
            scope.setTag("request.method", request.getMethod());
            scope.setLevel(io.sentry.SentryLevel.ERROR);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(ResultCode.SYSTEM_ERROR, "短信服务异常: " + e.getMessage());
    }

    /**
     * 处理OCR异常
     */
    @ExceptionHandler(OcrException.class)
    @ResponseStatus(HttpStatus.OK)
    public ApiResponse<Object> handleOcrException(OcrException e, HttpServletRequest request) {
        logger.error("OCR服务异常: {} - {}", request.getRequestURI(), e.getMessage(), e);

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "ocr");
            scope.setTag("request.uri", request.getRequestURI());
            scope.setTag("request.method", request.getMethod());
            scope.setLevel(io.sentry.SentryLevel.ERROR);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(ResultCode.SYSTEM_ERROR, "OCR服务异常: " + e.getMessage());
    }

    /**
     * 处理参数校验异常 - @Valid 注解校验失败
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String errorMessage = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining("; "));
        logger.warn("参数校验异常: {} - {}", request.getRequestURI(), errorMessage);

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "ValidationException");
            scope.setTag("http.method", request.getMethod());
            scope.setTag("request.uri", request.getRequestURI());
            scope.setExtra("errorMessage", errorMessage);
            scope.setExtra("fieldCount", String.valueOf(e.getBindingResult().getFieldErrors().size()));
            scope.setLevel(io.sentry.SentryLevel.WARNING);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(ResultCode.VALIDATION_ERROR, "参数校验失败: " + errorMessage);
    }

    /**
     * 处理绑定异常 - @Validated 注解校验失败
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Object> handleBindException(BindException e, HttpServletRequest request) {
        String errorMessage = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining("; "));
        logger.warn("绑定异常: {} - {}", request.getRequestURI(), errorMessage);
        return ApiResponse.fail(ResultCode.VALIDATION_ERROR, "参数绑定失败: " + errorMessage);
    }

    /**
     * 处理约束违反异常 - 单个参数校验失败
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Object> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        String errorMessage = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining("; "));
        logger.warn("约束违反异常: {} - {}", request.getRequestURI(), errorMessage);
        return ApiResponse.fail(ResultCode.VALIDATION_ERROR, "参数约束违反: " + errorMessage);
    }

    /**
     * 处理方法参数类型不匹配异常
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Object> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String message = String.format("参数 '%s' 类型错误，期望类型: %s", e.getName(), e.getRequiredType().getSimpleName());
        logger.warn("参数类型不匹配异常: {} - {}", request.getRequestURI(), message);
        return ApiResponse.fail(ResultCode.BAD_REQUEST, message);
    }

    /**
     * 处理不支持的HTTP方法异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public ApiResponse<Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        String message = String.format("不支持的请求方法: %s，支持的方法: %s", e.getMethod(), String.join(", ", e.getSupportedMethods()));
        logger.warn("不支持的HTTP方法异常: {} - {}", request.getRequestURI(), message);
        return ApiResponse.fail(ResultCode.METHOD_NOT_ALLOWED, message);
    }

    /**
     * 处理非法参数异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Object> handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
        logger.warn("非法参数异常: {} - {}", request.getRequestURI(), e.getMessage());

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "illegal_argument");
            scope.setTag("request.uri", request.getRequestURI());
            scope.setTag("request.method", request.getMethod());
            scope.setLevel(io.sentry.SentryLevel.WARNING);
            Sentry.captureException(e);
        });
        return ApiResponse.fail(ResultCode.BAD_REQUEST, e.getMessage());
    }

    /**
     * 处理系统异常
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiResponse<Object> handleException(Exception e, HttpServletRequest request) {
        logger.error("系统异常: {} - {}", request.getRequestURI(), e.getMessage(), e);

        // 记录异常日志
        try {
            SysUserEntity currentUser = getCurrentUser();
            String username = currentUser != null ? currentUser.getUsername() : "unknown";
            String realName = currentUser != null ? currentUser.getRealName() : "未知用户";
            sysErrorLogService.recordErrorLog(e, request, username, realName,
                "系统处理", "系统异常处理", "处理系统异常", null, "system", "系统异常");
        } catch (Exception logException) {
            logger.error("记录系统异常日志失败", logException);
        }

        // 发送到 Sentry
        Sentry.withScope(scope -> {
            scope.setTag("exception.type", "SystemException");
            scope.setTag("exception.class", e.getClass().getSimpleName());
            scope.setTag("http.method", request.getMethod());
            scope.setTag("request.uri", request.getRequestURI());
            scope.setExtra("exceptionClass", e.getClass().getSimpleName());
            scope.setExtra("message", e.getMessage() != null ? e.getMessage() : "Unknown error");
            scope.setExtra("timestamp", java.time.Instant.now().toString());
            scope.setLevel(io.sentry.SentryLevel.ERROR);
            Sentry.captureException(e);
        });

        return ApiResponse.fail(ResultCode.SYSTEM_ERROR, "系统处理异常，请稍后重试");
    }

    /**
     * 获取当前用户信息
     */
    private SysUserEntity getCurrentUser() {
        try {
            // 使用Sa-Token获取当前登录用户ID
            if (cn.dev33.satoken.stp.StpUtil.isLogin()) {
                Long userId = cn.dev33.satoken.stp.StpUtil.getLoginIdAsLong();
                if (userId != null) {
                    return sysUserService.getById(userId);
                }
            }
        } catch (Exception e) {
            logger.warn("获取当前用户信息失败", e);
        }
        return null;
    }
}